use std::ops::Deref;

use jwt_simple::prelude::*;

use crate::{error::AppError, User};

const JTE_DURATION: u64 = 60 * 60 * 24 * 7;

const ISSUER: &str = "chat_server";
const AUDIENCE: &str = "chat_web";

pub struct EncodingKey(Ed25519KeyPair);

pub struct DecodingKey(Ed25519PublicKey);

impl EncodingKey {
    // 加载私钥密文
    #[allow(dead_code)]
    pub fn load(pem: &str) -> Result<Self, AppError> {
        let key = Ed25519KeyPair::from_pem(pem)?;
        Ok(Self(key))
    }
    // 开始对用户信息进行签名
    #[allow(dead_code)]
    pub fn sign(&self, user: impl Into<User>) -> Result<String, AppError> {
        let claims = Claims::with_custom_claims(user.into(), Duration::from_secs(JTE_DURATION));
        let claims = claims.with_issuer(ISSUER).with_audience(AUDIENCE);
        let token = self.0.sign(claims)?;
        Ok(token)
    }
}

impl DecodingKey {
    // 加载公钥
    #[allow(dead_code)]
    pub fn load(pem: &str) -> Result<Self, AppError> {
        let key = Ed25519PublicKey::from_pem(pem)?;
        Ok(Self(key))
    }

    // 验证签名
    #[allow(dead_code)]
    pub fn verify(&self, token: &str) -> Result<User, AppError> {
        // 验证签名
        let ops = VerificationOptions {
            allowed_issuers: Some(HashSet::from_strings(&[ISSUER])),
            allowed_audiences: Some(HashSet::from_strings(&[AUDIENCE])),
            ..Default::default()
        };
        // ops.allowed_issuers = Some(HashSet::from_strings(&[ISSUER]));
        // ops.allowed_audiences = Some(HashSet::from_strings(&[AUDIENCE]));
        let claims = self.verify_token::<User>(token, Some(ops))?;
        Ok(claims.custom)
    }
}

// 实现解引用方法
impl Deref for EncodingKey {
    type Target = Ed25519KeyPair;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
// 实习那解引用方法
impl Deref for DecodingKey {
    type Target = Ed25519PublicKey;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// #[cfg(test)]
// mod tests {

//     use super::*;
//     use anyhow::Result;
//     use chrono::NaiveDateTime;

//     #[test]
//     fn test_jwt_should_success() -> Result<()> {
//         let pem = include_str!("../../fixtures/private.pem");
//         let key = EncodingKey::load(pem)?;
//         let hash_password = hash_password("123456");
//         let password_hash = hash_password?;
//         let user = User::new(
//             1,
//             "jinyulinlang".to_string(),
//             "jinyulinlang@qq.com".to_string(),
//             Some(password_hash),
//             NaiveDateTime::default(),
//             "jinyulinlang".to_string(),
//             NaiveDateTime::default(),
//             "jinyulinlang".to_string(),
//         );
//         let token = key.sign(user.clone())?;
//         let decode_key = DecodingKey::load(include_str!("../../fixtures/public.pem"))?;
//         let decode_user = decode_key.verify(token.as_str())?;
//         assert_eq!(user, decode_user);
//         Ok(())
//     }
// }
